package com.yaxin.heshanghui.customview.customLoading;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AnimationUtils;

import com.yaxin.heshanghui.R;
import com.yaxin.heshanghui.customview.customLoading.indicators.BallSpinFadeLoaderIndicator;

public class LoadingIndicatorView extends View
{

	private static final String TAG = "LoadingIndicatorView";

	private static final int MIN_SHOW_TIME = 500; // ms
	private static final int MIN_DELAY = 500; // ms

	private long mStartTime = -1;

	private boolean mPostedHide = false;

	private boolean mPostedShow = false;

	private boolean mDismissed = false;

	private final Runnable mDelayedHide = new Runnable()
	{

		@Override
		public void run()
		{
			mPostedHide = false;
			mStartTime = -1;
			setVisibility(View.GONE);
		}
	};

	private final Runnable mDelayedShow = new Runnable()
	{

		@Override
		public void run()
		{
			mPostedShow = false;
			if (!mDismissed)
			{
				mStartTime = System.currentTimeMillis();
				setVisibility(View.VISIBLE);
			}
		}
	};

	int mMinWidth;
	int mMaxWidth;
	int mMinHeight;
	int mMaxHeight;

	private Indicator mIndicator;
	private int mIndicatorColor;

	private boolean mShouldStartAnimationDrawable;

	public LoadingIndicatorView(Context context)
	{
		super(context);
		init(context, null, 0, 0);
	}

	public LoadingIndicatorView(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		init(context, attrs, 0, R.style.LoadingIndicatorView);
	}

	public LoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr)
	{
		super(context, attrs, defStyleAttr);
		init(context, attrs, defStyleAttr, R.style.LoadingIndicatorView);
	}

	@TargetApi(Build.VERSION_CODES.LOLLIPOP)
	public LoadingIndicatorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
	{
		super(context, attrs, defStyleAttr, defStyleRes);
		init(context, attrs, defStyleAttr, R.style.LoadingIndicatorView);
	}

	@SuppressWarnings("deprecation")
	private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
	{
		mMinWidth = 24;
		mMaxWidth = 48;
		mMinHeight = 24;
		mMaxHeight = 48;

		final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LoadingIndicatorView, defStyleAttr, defStyleRes);

		mMinWidth = a.getDimensionPixelSize(R.styleable.LoadingIndicatorView_minWidth, mMinWidth);
		mMaxWidth = a.getDimensionPixelSize(R.styleable.LoadingIndicatorView_maxWidth, mMaxWidth);
		mMinHeight = a.getDimensionPixelSize(R.styleable.LoadingIndicatorView_minHeight, mMinHeight);
		mMaxHeight = a.getDimensionPixelSize(R.styleable.LoadingIndicatorView_maxHeight, mMaxHeight);
		String indicatorName = a.getString(R.styleable.LoadingIndicatorView_indicatorName);
		mIndicatorColor = a.getColor(R.styleable.LoadingIndicatorView_indicatorColor, getResources().getColor(R.color.colorAccent));
		setIndicator(indicatorName);
		if (mIndicator == null)
		{
			setIndicator(BallSpinFadeLoaderIndicator.class);
		}
		a.recycle();
	}

	public Indicator getIndicator()
	{
		return mIndicator;
	}

	public void setIndicator(Indicator d)
	{
		if (mIndicator != d)
		{
			if (mIndicator != null)
			{
				mIndicator.setCallback(null);
				unscheduleDrawable(mIndicator);
			}

			mIndicator = d;
			//need to set indicator color again if you didn't specified when you update the indicator .
			setIndicatorColor(mIndicatorColor);
			if (d != null)
			{
				d.setCallback(this);
			}
			postInvalidate();
		}
	}

	/**
	 * setIndicatorColor(0xFF00FF00)
	 * or
	 * setIndicatorColor(Color.BLUE)
	 * or
	 * setIndicatorColor(Color.parseColor("#FF4081"))
	 * or
	 * setIndicatorColor(0xFF00FF00)
	 * or
	 * setIndicatorColor(getResources().getColor(android.R.color.black))
	 *
	 * @param color color
	 */
	public void setIndicatorColor(@ColorInt int color)
	{
		this.mIndicatorColor = color;
		mIndicator.setColor(color);
	}

	/**
	 * You should pay attention to pass this parameter with two way:
	 * for example:
	 * 1. Only class Name,like "SimpleIndicator".(This way would use default package name with
	 * "com.wang.avi.indicators")
	 * 2. Class name with full package,like "com.my.android.indicators.SimpleIndicator".
	 *
	 * @param indicatorName the class must be extend Indicator .
	 */
	public void setIndicator(String indicatorName)
	{
		if (TextUtils.isEmpty(indicatorName))
		{
			return;
		}
		StringBuilder drawableClassName = new StringBuilder();
		if (!indicatorName.contains("."))
		{
			String defaultPackageName = getClass().getPackage().getName();
			drawableClassName.append(defaultPackageName).append(".indicators").append(".");
		}
		drawableClassName.append(indicatorName);
		try
		{
			Class<?> drawableClass = Class.forName(drawableClassName.toString());
			Indicator indicator = (Indicator) drawableClass.newInstance();
			setIndicator(indicator);
		} catch (ClassNotFoundException e)
		{
			Log.e(TAG, "Didn't find your class , check the name again !");
		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	public void smoothToShow()
	{
		startAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_in));
		setVisibility(VISIBLE);
	}

	public void smoothToHide()
	{
		startAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_out));
		setVisibility(GONE);
	}

	public void hide()
	{
		mDismissed = true;
		removeCallbacks(mDelayedShow);
		long diff = System.currentTimeMillis() - mStartTime;
		if (diff >= MIN_SHOW_TIME || mStartTime == -1)
		{
			// The progress spinner has been shown long enough
			// OR was not shown yet. If it wasn't shown yet,
			// it will just never be shown.
			setVisibility(View.GONE);
		} else
		{
			// The progress spinner is shown, but not long enough,
			// so put a delayed message in to hide it when its been
			// shown long enough.
			if (!mPostedHide)
			{
				postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
				mPostedHide = true;
			}
		}
	}

	public void show()
	{
		// Reset the start time.
		mStartTime = -1;
		mDismissed = false;
		removeCallbacks(mDelayedHide);
		if (!mPostedShow)
		{
			postDelayed(mDelayedShow, MIN_DELAY);
			mPostedShow = true;
		}
	}

	@Override
	protected boolean verifyDrawable(@NonNull Drawable who)
	{
		return who == mIndicator || super.verifyDrawable(who);
	}

	private void startAnimation()
	{
		if (getVisibility() != VISIBLE)
		{
			return;
		}

		if (mIndicator != null)
		{
			mShouldStartAnimationDrawable = true;
		}
		postInvalidate();
	}

	private void stopAnimation()
	{
		if (mIndicator != null)
		{
			mIndicator.stop();
			mShouldStartAnimationDrawable = false;
		}
		postInvalidate();
	}

	@Override
	public void setVisibility(int v)
	{
		if (getVisibility() != v)
		{
			super.setVisibility(v);
			if (v == GONE || v == INVISIBLE)
			{
				stopAnimation();
			} else
			{
				startAnimation();
			}
		}
	}

	@Override
	protected void onVisibilityChanged(@NonNull View changedView, int visibility)
	{
		super.onVisibilityChanged(changedView, visibility);
		if (visibility == GONE || visibility == INVISIBLE)
		{
			stopAnimation();
		} else
		{
			startAnimation();
		}
	}

	@Override
	public void invalidateDrawable(@NonNull Drawable dr)
	{
		if (verifyDrawable(dr))
		{
			final Rect dirty = dr.getBounds();
			final int scrollX = getScrollX() + getPaddingLeft();
			final int scrollY = getScrollY() + getPaddingTop();

			invalidate(dirty.left + scrollX, dirty.top + scrollY, dirty.right + scrollX, dirty.bottom + scrollY);
		} else
		{
			super.invalidateDrawable(dr);
		}
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh)
	{
		updateDrawableBounds(w, h);
	}

	private void updateDrawableBounds(int w, int h)
	{
		// onDraw will translate the canvas so we draw starting at 0,0.
		// Subtract out padding for the purposes of the calculations below.
		w -= getPaddingRight() + getPaddingLeft();
		h -= getPaddingTop() + getPaddingBottom();

		int right = w;
		int bottom = h;
		int top = 0;
		int left = 0;

		if (mIndicator != null)
		{
			// Maintain aspect ratio. Certain kinds of animated drawables
			// get very confused otherwise.
			final int intrinsicWidth = mIndicator.getIntrinsicWidth();
			final int intrinsicHeight = mIndicator.getIntrinsicHeight();
			final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight;
			final float boundAspect = (float) w / h;
			if (intrinsicAspect != boundAspect)
			{
				if (boundAspect > intrinsicAspect)
				{
					// New width is larger. Make it smaller to match height.
					final int width = (int) (h * intrinsicAspect);
					left = (w - width) / 2;
					right = left + width;
				} else
				{
					// New height is larger. Make it smaller to match width.
					final int height = (int) (w * (1 / intrinsicAspect));
					top = (h - height) / 2;
					bottom = top + height;
				}
			}
			mIndicator.setBounds(left, top, right, bottom);
		}
	}

	@Override
	protected synchronized void onDraw(Canvas canvas)
	{
		super.onDraw(canvas);
		drawTrack(canvas);
	}

	void drawTrack(@NonNull Canvas canvas)
	{
		final Drawable d = mIndicator;
		if (d != null)
		{
			// Translate canvas so a indeterminate circular progress bar with padding
			// rotates properly in its animation
			final int saveCount = canvas.save();

			canvas.translate(getPaddingLeft(), getPaddingTop());

			d.draw(canvas);
			canvas.restoreToCount(saveCount);

			if (mShouldStartAnimationDrawable && d instanceof Animatable)
			{
				((Animatable) d).start();
				mShouldStartAnimationDrawable = false;
			}
		}
	}

	@Override
	protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		int dw = 0;
		int dh = 0;

		final Drawable d = mIndicator;
		if (d != null)
		{
			dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));
			dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));
		}

		updateDrawableState();

		dw += getPaddingLeft() + getPaddingRight();
		dh += getPaddingTop() + getPaddingBottom();

		final int measuredWidth = resolveSizeAndState(dw, widthMeasureSpec, 0);
		final int measuredHeight = resolveSizeAndState(dh, heightMeasureSpec, 0);
		setMeasuredDimension(measuredWidth, measuredHeight);
	}

	@Override
	protected void drawableStateChanged()
	{
		super.drawableStateChanged();
		updateDrawableState();
	}

	private void updateDrawableState()
	{
		final int[] state = getDrawableState();
		if (mIndicator != null && mIndicator.isStateful())
		{
			mIndicator.setState(state);
		}
	}

	@TargetApi(Build.VERSION_CODES.LOLLIPOP)
	@Override
	public void drawableHotspotChanged(float x, float y)
	{
		super.drawableHotspotChanged(x, y);

		if (mIndicator != null)
		{
			mIndicator.setHotspot(x, y);
		}
	}

	@Override
	protected void onAttachedToWindow()
	{
		super.onAttachedToWindow();
		startAnimation();
		removeCallbacks();
	}

	@Override
	protected void onDetachedFromWindow()
	{
		stopAnimation();
		// This should come after stopAnimation(), otherwise an invalidate message remains in the
		// queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
		super.onDetachedFromWindow();
		removeCallbacks();
	}

	private void removeCallbacks()
	{
		removeCallbacks(mDelayedHide);
		removeCallbacks(mDelayedShow);
	}

	public void setIndicator(@NonNull Class cls)
	{
		Indicator indicator = null;
		try
		{
			indicator = (Indicator) cls.newInstance();
		} catch (Exception e)
		{
			e.printStackTrace();
		}
		setIndicator(indicator);
	}

}
